package com.hhd.pdf.variable;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.hhd.pdf.CommonUtils;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;

@Getter
@Setter
@Builder
public class VariableContext {
    VariablePath basePath;
    JSONObject container;
    VariableContext parent;

    public VariableContext child(JSONObject container, String field) {
        VariablePath path = basePath.clone();
        path.addPath(field);
        return VariableContext.builder()
                .basePath(path)
                .container(container)
                .parent(this)
                .build();
    }

    public void execute(Formula formula) {
        VariablePath variablePath = formula.createVariableFormula();
        if (variablePath.isSample(this.basePath)) {
            formula.setFormulaValue(container);
            return;
        }

        if (this.getBasePath().isSuper(variablePath)) {
            deepExecute(formula, variablePath);
            return;
        }

        if (isVariableInContext(variablePath)) {
            simpleExecute(formula, variablePath);
            return;
        }

        VariableContext tempParent = parent;
        while (!CommonUtils.isEmpty(tempParent)) {
            if (tempParent.isVariableInContext(variablePath)) {
                tempParent.simpleExecute(formula, variablePath);
                return;
            }
            tempParent = tempParent.parent;
        }

        VariablePath tempVariablePath = variablePath.clone();
        do {
            tempVariablePath = tempVariablePath.getParent();
            tempParent = parent;
            while (!CommonUtils.isEmpty(tempParent)) {
                if (tempParent.isVariableInContext(tempVariablePath)) {
                    tempParent.deepExecute(formula, variablePath);
                    return;
                }
                tempParent = tempParent.parent;
            }
        } while (!tempVariablePath.isRoot());
    }

    private void deepExecute(Formula formula, VariablePath variableFormula) {
        Iterator<String> iterator = this.basePath.getPath().iterator();
        Iterator<String> iterator1 = variableFormula.getPath().iterator();
        while (iterator.hasNext()) {
            iterator.next();
            iterator1.next();
        }

        JSONObject tempContainer = container;
        while (iterator1.hasNext()) {
            String fieldName = iterator1.next();
            Object o = getObjeftFromJSON(tempContainer, fieldName);
            if (CommonUtils.isEmpty(o)) return;

            if (o instanceof JSONObject) {
                tempContainer = (JSONObject) o;
            } else if (o instanceof JSONArray) {
                JSONArray array = (JSONArray) o;
                if (CommonUtils.isEmpty(array)) return;
                tempContainer = array.getJSONObject(0);

                if (!iterator1.hasNext()) {
                    formula.setFormulaValue(array);
                }
            } else if (!iterator1.hasNext()) {
                formula.setFormulaValue(o);
            }
        }
    }

    private Object getObjeftFromJSON(JSONObject tempContainer, String field) {
        Object o = tempContainer.get(field);
        if (!CommonUtils.isEmpty(o)) {
            if (o instanceof String) {
                String str = (String) o;
                if(CommonUtils.isEmpty(str)) return str;

                try {
                    return JSONArray.parseArray(str);
                } catch (Exception e) {
                }
                try {
                    return JSONObject.parseObject(str);
                } catch (Exception e) {
                }
            }
        }
        return o;
    }


    private void simpleExecute(Formula formula, VariablePath variableFormula) {
        String fieldName = variableFormula.fieldName();
        formula.setFormulaValue(container.get(fieldName));
    }

    private boolean isVariableInContext(VariablePath variableFormula) {
        return basePath.isSample(variableFormula.getParent());
    }

    public List<VariableContext> subContextByFormula(ParsedFormula formula) {
        return subContextByFormula(formula, true);
    }

    public List<VariableContext> subContextByFormula(ParsedFormula formula, boolean forParent) {
        VariablePath variablePath = formula.createVariableFormula();
        if (forParent) {
            variablePath = formula.createVariableFormula().getParent();
        }

        LinkedList<VariableContext> level = new LinkedList<>();
        level.add(this);

        LinkedList<String> substract = variablePath.substract(this.basePath);
        Iterator<String> iterator = substract.iterator();
        while (iterator.hasNext()) {
            String field = iterator.next();
            LinkedList<VariableContext> temp = new LinkedList<>();
            for (VariableContext context : level) {
                Object o = getValueForJSON(field, context);
                if (o instanceof JSONObject) {
                    temp.add(context.child((JSONObject) o, field));
                } else if (o instanceof JSONArray) {
                    for (Object object : (JSONArray) o) {
                        if (object instanceof JSONObject) {
                            temp.add(context.child((JSONObject) object, field));
                        }
                    }
                }
            }
            level = temp;
        }

        return level;
    }

    private Object getValueForJSON(String field, VariableContext context) {
        Object o = context.getContainer().get(field);
        if (!CommonUtils.isEmpty(o)) {
            if (o instanceof String) {
                String str = (String) o;
                if(CommonUtils.isEmpty(str)) return str;

                try {
                    return JSONArray.parseArray(str);
                } catch (Exception e) {
                }
                try {
                    return JSONObject.parseObject(str);
                } catch (Exception e) {
                }
            }
        }
        return o;
    }

    public boolean has(String formula) {
        ParsedFormula parsedFormula = ParsedFormula.formula(formula);
        Object result = this.deepExecuteHas(parsedFormula, parsedFormula.createVariableFormula());
        if ("".equals(result) || Objects.isNull(result)) {
            return false;
        }
        if (result instanceof List) {
            List list = (List) result;
            if (CommonUtils.isEmpty(list)) {
                return false;
            }
        }
        return true;
    }

    private Object deepExecuteHas(ParsedFormula formula, VariablePath variableFormula) {
        if(variableFormula.isSample(this.basePath)) return this;
        Iterator<String> iterator = this.basePath.getPath().iterator();
        Iterator<String> iterator1 = variableFormula.getPath().iterator();
        while (iterator.hasNext()) {
            iterator.next();
            iterator1.next();
        }

        JSONObject tempContainer = container;
        while (iterator1.hasNext()) {
            String fieldName = iterator1.next();
            Object o = getObjeftFromJSON(tempContainer, fieldName);
            if (CommonUtils.isEmpty(o)) return null;

            if (!iterator1.hasNext()) {
                return o;
            }

            if (o instanceof JSONObject) {
                tempContainer = (JSONObject) o;
            } else if (o instanceof JSONArray) {
                JSONArray array = (JSONArray) o;
                if (CommonUtils.isEmpty(array)) return o;
                tempContainer = array.getJSONObject(0);
            }
        }
        return null;
    }

    public List<VariableContext> brothersContextByFormula(ParsedFormula formula) {
        VariableContext tempContext = this;

        while (!tempContext.getBasePath().isRoot()) {
            VariablePath variablePath = formula.getVariablePath();
            while (!variablePath.isRoot()) {
                if (variablePath.isSample(tempContext.getBasePath())) {
                    return CommonUtils.newList(tempContext);
                }
                variablePath = variablePath.getParent();
            }
            tempContext = tempContext.getParent();
        }
        return CommonUtils.newList();
    }
}
